---
title: Deploy to a VM
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

This guide helps you to set up production-ready LiveKit server on a virtual machine.

This configuration uses Docker Compose and Caddy. Your LiveKit server will support
a wide range of connectivity, including those behind VPN and firewalls (via TURN/TLS)

You do not need separate SSL certificates for this set up, we will provision them
automatically with Caddy.

## Pre-requisites

To start, you'll need:

- A domain you control
- The ability to add DNS records for subdomains for your new LiveKit server

## Generate configuration

Use our configuration generation tool to create a customized configuration for your domain.

```shell
docker run --rm -it -v$PWD:/output livekit/generate
```

You should have a folder with the following files:

* caddy.yaml
* docker-compose.yaml
* livekit.yaml
* redis.conf
* init_script.sh OR cloud_init.xxxx.yaml

## Deploy to a VM

Depending on your cloud provider, there are a couple of options:

<Tabs
  defaultValue="cloud-init"
  groupId="cloud-deploy"
  values={[
    {label: 'Cloud Init', value: 'cloud-init'},
    {label: 'Startup Script', value: 'startup-script'},
  ]}>

  <TabItem value="cloud-init">

  This is the easiest method for deploying LiveKit Server. AWS, Azure, Digital Ocean, and others support [cloud-init](https://cloud-init.io/).

  We have tested our scripts on Ubuntu and Amazon Linux, but it's possible the same scripts may work on other platforms.
  (Please let us know in Slack or open a PR!)

  When starting a VM, paste the contents of the file `cloud-init.xxxx.yaml` into the `User data` field.

  That's it! When the machine starts up, it'll execute the cloud-init protocol and install LiveKit.

  </TabItem>
  <TabItem value="startup-script">

  We could also generate a startup script that works when cloud-init isn't supported.

  This has been tested with Linode and Google Cloud.

  1. Start a VM as usual
  2. Copy the file `init_script.sh` to the VM
  3. ssh into the instance
  3. Run `sudo ./init_script.sh` to perform installation


  </TabItem>
</Tabs>

When the install script is finished, your instance should be set up. It will have installed:

* docker
* docker-compose
* generated configuration to /opt/livekit
* systemd service `livekit-docker`

To start/stop the service you could use systemctl:

```shell
systemctl stop livekit-docker

systemctl start livekit-docker
```

## Firewall

Ensure that the following ports are open on your firewall and accessible on the instance:

 * 443 - primary HTTPS and TURN/TLS
 * 80 - TLS issuance
 * 7881 - WebRTC over TCP
 * 443/UDP - TURN/UDP
 * 50000-60000/UDP - WebRTC over UDP

## DNS

Ensure both primary domain and TURN domain are pointed at the IP address of your instance.

This is required for Caddy to start provisioning your TLS certificates.

## Troubleshooting

If something is not working as expected, SSH in to your server and use the following commands to investigate:

```shell
systemctl status livekit-docker
cd /opt/livekit
sudo docker-compose logs
```

### Checking TLS certificates

If certificate acquisition process has been successful, you should see the following log entry:

```
livekit-caddy-1    | {"level":"info","ts":1642786068.3883107,"logger":"tls.obtain","msg":"certificate obtained successfully","identifier":"<yourhost>"}
```

If you don't see these messages, it means your server could not be reached from the internet.

### Ensure DNS is pointed at your domain

Running `host <yourdomain>` should show the IP address of your server. Ensure that it matches the IP address of your server.

### Instance firewall

Certain Linux distributions ship with an instance-specific firewall enabled. To check if this is the case, run:

```
sudo firewall-cmd --list-all
```

If firewall is enabled, you could add the following rules to it and restart the firewall:

```
sudo firewall-cmd --zone=public --permanent --add-port=80/tcp
sudo firewall-cmd --zone=public --permanent --add-port=443/tcp
sudo firewall-cmd --zone=public --permanent --add-port=7881/tcp
sudo firewall-cmd --zone=public --permanent --add-port=443/udp
sudo firewall-cmd --zone=public --permanent --add-port=50000-60000/udp
sudo firewall-cmd --reload
```

When the ports are successfully opened, running `curl http://<yourdomain>` should return a 404 response. (instead of hanging)